/**
 * Function
 *
 * (param1_name: type1, param2_name: type2, ...) => type
 */
function literalFunction() {
  // 匿名函数
  const anonymousFunc = function(a: number, b: number): number {
    return a + b;
  };
  // 箭头函数
  const arrowFunc = (a: number, b: number): number => {
    return a + b;
  };
}

function variableFunction() {

}





/**
 * 泛型函数 => 可变的参数类型
 *
 * 编写泛型函数的最佳实践
 * (1) Push Type Parameters Down
 * (2) Use Fewer Type Parameters
 * (3) Type Parameters should appear twice
 */
// 1.泛型函数初始化
// 普通泛型函数
function map<Input, Output>(arr: Input[], func: (arg: Input) => Output): Output[] {
  return arr.map(func);
}

function combine<T>(arr1: T[], arr2: T[]): T[] {
  return arr1.concat(arr2);
}

// 受约束泛型函数
// 类型参数不能等同于受约束条件
function longest<T extends { length: number }>(a: T, b: T): T {
  return a.length >= b.length ? a : b;
}
function minimumLength<T extends { length: number }>(obj: T, minium: number): T {
  if (obj.length >= minium) {
    return obj;
  } else {
    return { length: minium }; // compile is error
  }
}

// 2. 泛型函数使用
// 编译器推断类型参数值
const parsed = map(['1', '2', '3'], n => Number.parseInt(n));

// 手动赋予类型参数值
// const arr = combine([1, 2, 3], ['hello']); // compile is error
const arr = combine<number | string>([1, 2, 3], ['hello']);





/**
 * 可选参数 => 可变的参数数量
 *
 * x?: number ==> x: number | undefined ==> x: number | undefined = undefined
 */
function optionParameterWithDefault(x: number = 10): void {
  console.log(x);
}

// function optionParameterWithUndefined(x: number | undefined): void {
//   console.log(x);
// }
function optionParameterWithUndefined(x?: number): void {
  console.log(x);
}





/**
 * 剩余参数 => 可变的参数数量
 */
function multiply(n: number, ...m: number[]): number[] {
  return m.map(x => x * n);
}

const mutiA = multiply(10, 1, 2, 3, 4);
const mutiB = multiply(10, ...[1, 2, 3, 4]);





/**
 * 函数重载 => 可变的参数类型和数量
 *
 * 编写函数重载最佳实践
 *  (1)尽量用 Union 类型或泛型或可选参数来代替函数重载
 *  (2)函数重载的实现要兼容所有的重载
 */
function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
  if (d !== undefined && y !== undefined) {
    return new Date(y, mOrTimestamp, d);
  } else {
    return new Date(mOrTimestamp);
  }
}
